import { Injectable } from '@nestjs/common';
import { UserAdminEntity } from './user-admin.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { UserAdminDto } from './user-admin.model';
import { randomValue } from '~/utils';
import { isEmpty, isNil } from 'lodash';
import { BusinessException } from '~/common/exceptions/biz.exception';
import { ErrorEnum } from '~/constants/error-code.constant';
import { TenantEntity } from '~/modules/tenant/tenant.entity';
import { TenantService } from '~/modules/tenant/tenant.service';

@Injectable()
export class UserAdminService {
  constructor(
    @InjectRepository(UserAdminEntity)
    private readonly userAdminRepo: Repository<UserAdminEntity>,
    private readonly tenantService: TenantService
  ) {}

  /**
   * 判断用户名是否存在
   */
  async validExist(username: string): Promise<void> {
    const user = await this.userAdminRepo.findOneBy({ username });

    if (!isNil(user)) {
      throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
    }
  }

  async create(user: UserAdminDto): Promise<UserAdminEntity> {
    await this.validExist(user.username);

    const psalt = randomValue(32);
    return this.userAdminRepo.save({
      ...user,
      psalt,
    });
  }

  async createBindTenant(
    username: string,
    password: string,
    tenantId?: number
  ): Promise<UserAdminEntity> {
    await this.validExist(username);

    let tenants: TenantEntity[] = [];

    if (tenantId) {
      const tenant = await this.tenantService.getTenantById(tenantId);

      if (isNil(tenant)) {
        throw new BusinessException(ErrorEnum.NO_TENANT);
      }

      tenants.push(tenant);
    }

    const psalt = randomValue(32);
    return this.userAdminRepo.save({
      username,
      password,
      psalt,
      tenants,
    });
  }

  async update(id: number, user: UserAdminDto): Promise<UserAdminEntity> {
    await this.userAdminRepo.update(id, user);
    return this.getById(id);
  }

  async delete(id: number): Promise<void> {
    await this.userAdminRepo.delete(id);
  }

  async getList(): Promise<UserAdminEntity[]> {
    return this.userAdminRepo.find();
  }

  async getById(id: number): Promise<UserAdminEntity> {
    return this.userAdminRepo.findOneBy({ id });
  }

  async getByUsername(username: string): Promise<UserAdminEntity> {
    return this.userAdminRepo.findOne({ where: { username } });
  }

  async bindAdminToTenant(tenantId: number, tenant: TenantEntity): Promise<void> {
    const row = await this.getById(tenantId);

    if (isNil(row)) {
      throw new BusinessException(ErrorEnum.ADMIN_NOT_FOUND);
    }

    await this.userAdminRepo.update(tenantId, { tenants: [tenant] });
  }
}
